﻿#include "MapForm.h"
#include "../../Server/MirDatabase/MapInfo.h"
#include "../../Shared/Enums.h"
#include "../../Server/MirDatabase/MovementInfo.h"
#include "../../Server/MirDatabase/MineInfo.h"
#include "../../Server/MirDatabase/SafeZoneInfo.h"
#include "../Server.SMain.h"

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;

namespace Server::MirForms
{

Envir *ConvertMapInfo::EditEnvir = nullptr;
int ConvertMapInfo::_endIndex = 0;
std::string ConvertMapInfo::Path = "";
std::vector<std::string> ConvertMapInfo::errors;

    void ConvertMapInfo::Start(Envir *envirToUpdate)
    {
        if (Path == "")
        {
            return;
        }
        EditEnvir = envirToUpdate;

        if (EditEnvir == nullptr)
        {
            return;
        }

        auto lines = File::ReadAllLines(Path);
        _endIndex = EditEnvir->MapIndex; // Last map index number
        for (int i = 0; i < lines.size(); i++)
        {

            if (StringHelper::startsWith(lines[i], "[")) // Read map info
            {
                lines[i] = System::Text::RegularExpressions::Regex::Replace(lines[i], R"(\s+)", " "); // Clear white-space
                lines[i] = StringHelper::replace(lines[i], " ;", ";"); // Remove space before semi-colon

                // Trim comment at the end of the line
                if (lines[i].find(';') != std::string::npos)
                {
                    lines[i] = lines[i].substr(0, (int)lines[i].find(";", System::StringComparison::Ordinal));
                }

                MirDatabase::MapInfo *newMapInfo = new MirDatabase::MapInfo();
                newMapInfo->Index = ++_endIndex;

                auto a = StringHelper::split(lines[i], ']'); // Split map info into [0] = MapFile MapName 0 || [1] = Attributes
                std::vector<std::string> b = StringHelper::split(a[0], ' ');

                newMapInfo->FileName = StringHelper::trimStart(b[0], "["); // Assign MapFile from variable and trim leading '[' char
                newMapInfo->Title = b[1]; // Assign MapName from variable

                std::vector<std::string> mapAttributes; // Group of all attributes associated with that map
                mapAttributes.AddRange(StringHelper::split(a[1], ' '));

                newMapInfo->NoTeleport = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOTELEPORT"));
                });
                newMapInfo->NoRandom = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NORANDOMMOVE"));
                });
                newMapInfo->NoEscape = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOESCAPE"));
                });
                newMapInfo->NoRecall = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NORECALL"));
                });
                newMapInfo->NoDrug = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NODRUG"));
                });
                newMapInfo->NoPosition = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOPOSITIONMOVE"));
                });
                newMapInfo->NoThrowItem = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOTHROWITEM"));
                });
                newMapInfo->NoDropPlayer = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOPLAYERDROP"));
                });
                newMapInfo->NoDropMonster = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOMONSTERDROP"));
                });
                newMapInfo->NoNames = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NONAMES"));
                });
                newMapInfo->NoFight = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOFIGHT"));
                });
                newMapInfo->NoMount = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOMOUNT"));
                });
                newMapInfo->NeedBridle = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NEEDBRIDLE"));
                });
                newMapInfo->Fight = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("FIGHT"));
                });
                newMapInfo->NoTownTeleport = mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("NOTOWNTELEPORT"));
                });

                newMapInfo->Fire = mapAttributes.Any([&] (std::any x)
                {
                    x->StartsWith(StringHelper::toUpper("FIRE(")));
                };
                if (newMapInfo->Fire)
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("FIRE(")));
                    };
                    newMapInfo->FireDamage = static_cast<short>(StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("FIRE(")).begin(), (std::string("FIRE(")).end())), ")"));
                }
                newMapInfo->Lightning = mapAttributes.Any([&] (std::any x)
                {
                    x->StartsWith(StringHelper::toUpper("LIGHTNING(")));
                };
                if (newMapInfo->Lightning)
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("LIGHTNING(")));
                    };
                    newMapInfo->LightningDamage = static_cast<short>(StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("LIGHTNING(")).begin(), (std::string("LIGHTNING(")).end())), ")"));
                }
                newMapInfo->NoReconnect = mapAttributes.Any([&] (std::any x)
                {
                    x->StartsWith(StringHelper::toUpper("NORECONNECT(")));
                };
                if (newMapInfo->NoReconnect)
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("NORECONNECT(")));
                    };
                    newMapInfo->NoReconnectMap = newMapInfo->NoReconnectMap == "" ? "0" : StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("NORECONNECT(")).begin(), (std::string("NORECONNECT(")).end())), ")");
                }

                if (mapAttributes.Any([&] (std::any x)
                {
                    x->StartsWith(StringHelper::toUpper("MINIMAP(")));
                })
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("MINIMAP(")));
                    };
                    newMapInfo->MiniMap = static_cast<unsigned short>(StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("MINIMAP(")).begin(), (std::string("MINIMAP(")).end())), ")"));
                }
                if (mapAttributes.Any([&] (std::any x)
                {
                    x->StartsWith(StringHelper::toUpper("BIGMAP(")));
                })
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("BIGMAP(")));
                    };
                    newMapInfo->BigMap = static_cast<unsigned short>(StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("BIGMAP(")).begin(), (std::string("BIGMAP(")).end())), ")"));
                }
                if (mapAttributes.Any([&] (std::any s)
                {
                    s->Contains(StringHelper::toUpper("MINE(")));
                })
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("MINE(")));
                    };
                    newMapInfo->MineIndex = static_cast<unsigned char>(StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("MINE(")).begin(), (std::string("MINE(")).end())), ")"));
                }
                if (mapAttributes.Any([&] (std::any s)
                {
                    s->Contains(StringHelper::toUpper("MAPLIGHT(")));
                })
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("MAPLIGHT(")));
                    };
                    newMapInfo->MapDarkLight = static_cast<unsigned char>(StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("MAPLIGHT(")).begin(), (std::string("MAPLIGHT(")).end())), ")"));
                }
                if (mapAttributes.Any([&] (std::any s)
                {
                    s->Contains(StringHelper::toUpper("MUSIC(")));
                })
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("MUSIC(")));
                    };
                    newMapInfo->Music = static_cast<unsigned short>(StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("MUSIC(")).begin(), (std::string("MUSIC(")).end())), ")"));
                }
                if (mapAttributes.Any([&] (std::any x)
                {
                    x->StartsWith(StringHelper::toUpper("LIGHT(")));
                }) // Check if there is a LIGHT attribute and get its value
                {
                    int index = mapAttributes.FindIndex([&] (std::any x)
                    {
                        x->StartsWith(StringHelper::toUpper("LIGHT(")));
                    };
//C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string and was converted to C++ 'if-else' logic:
//                    switch (mapAttributes[index].TrimStart("LIGHT(".ToCharArray()).TrimEnd(')'))
//ORIGINAL LINE: case "Dawn":
                    if (StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("LIGHT(")).begin(), (std::string("LIGHT(")).end())), ")") == "Dawn")
                    {
                            newMapInfo->Light = LightSetting::Dawn;

                            delete newMapInfo;
                    }
//ORIGINAL LINE: case "Day":
                    else if (StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("LIGHT(")).begin(), (std::string("LIGHT(")).end())), ")") == "Day")
                    {
                            newMapInfo->Light = LightSetting::Day;
                    }
//ORIGINAL LINE: case "Evening":
                    else if (StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("LIGHT(")).begin(), (std::string("LIGHT(")).end())), ")") == "Evening")
                    {
                            newMapInfo->Light = LightSetting::Evening;
                    }
//ORIGINAL LINE: case "Night":
                    else if (StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("LIGHT(")).begin(), (std::string("LIGHT(")).end())), ")") == "Night")
                    {
                            newMapInfo->Light = LightSetting::Night;
                    }
//ORIGINAL LINE: case "Normal":
                    else if (StringHelper::trimEnd(mapAttributes[index].TrimStart(std::vector<char>((std::string("LIGHT(")).begin(), (std::string("LIGHT(")).end())), ")") == "Normal")
                    {
                            newMapInfo->Light = LightSetting::Normal;
                    }
                    else
                    {
                            newMapInfo->Light = LightSetting::Normal;
                    }
                }
                else
                {
                    newMapInfo->Light = LightSetting::Normal;
                }

                // Check for light type
                if (mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("DAY"));
                })) // DAY = Day
                {
                    newMapInfo->Light = LightSetting::Day;
                }
                else if (mapAttributes.Any([&] (std::any s)
                {
                    return s->Contains(StringHelper::toUpper("DARK"));
                })) // DARK = Night
                {
                    newMapInfo->Light = LightSetting::Night;
                }

                EditEnvir->MapInfoList.push_back(newMapInfo); // Add map to list

//C# TO C++ CONVERTER TODO TASK: A 'delete newMapInfo' statement was not added since newMapInfo was passed to a method or constructor. Handle memory management manually.
            }
            else if (StringHelper::startsWith(lines[i], ";"))
            {
                continue;
            }
            else
            {
                errors.push_back("Error on Line " + std::to_string(i) + ": " + lines[i] + "");
            }
        }

        for (int j = 0; j < EditEnvir->MapInfoList.size(); j++)
        {
            for (int k = 0; k < lines.size(); k++)
            {
                try
                {
                    if (StringHelper::startsWith(lines[k], EditEnvir->MapInfoList[j]->FileName + " "))
                    {
                        MirDatabase::MovementInfo *newMovement = new MirDatabase::MovementInfo();

                        if (lines[k].find("NEEDHOLE") != std::string::npos)
                        {
                            newMovement->NeedHole = true;
                            lines[k] = StringHelper::replace(lines[k], "NEEDHOLE", "");
                        }
                        if (lines[k].find("NEEDMOVE") != std::string::npos)
                        {
                            newMovement->NeedMove = true;
                            lines[k] = StringHelper::replace(lines[k], "NEEDMOVE", "");
                        }
                        if (lines[k].find("NEEDCONQUEST") != std::string::npos)
                        {
                            int conqLocation = (int)lines[k].find(" NEEDCONQUEST");
                            std::string conq = lines[k].substr(conqLocation);
                            int conqIndex = std::stoi(StringHelper::replace(StringHelper::replace(conq, "NEEDCONQUEST(", ""), ")", "")); //get value
                            newMovement->ConquestIndex = conqIndex;
                            lines[k].erase(conqLocation);
                        }

                        lines[k] = StringHelper::replace(lines[k], '.', ','); // Replace point with comma
                        lines[k] = StringHelper::replace(lines[k], ":", ","); // Replace colon with comma
                        lines[k] = StringHelper::replace(lines[k], ", ", ","); // Remove space after comma
                        lines[k] = StringHelper::replace(lines[k], " ,", ","); // Remove space before comma
                        lines[k] = System::Text::RegularExpressions::Regex::Replace(lines[k], R"(\s+)", " "); // Clear whitespace
                        lines[k] = StringHelper::replace(lines[k], " ;", ";"); // Remove space before semi-colon

                        // Trim comment at the end of the line
                        if (lines[k].find(';') != std::string::npos)
                        {
                            lines[k] = lines[k].substr(0, (int)lines[k].find(";", System::StringComparison::Ordinal));
                        }

                        auto c = StringHelper::split(lines[k], ' ');

                        // START - Get values from line
                        if (c.size() == 7) // Every value has a space
                        {
                            c[1] = c[1] + "," + c[2];
                            c[2] = c[5] + "," + c[6];
                            c[3] = c[4];
                        }
                        else if (c.size() == 6) // One value has a space
                        {
                            if (c[2] == "->") // Space in to XY
                            {
                                c[2] = c[4] + "," + c[5];
                            }
                            else if (c[3] == "->") // Space in from XY
                            {
                                c[1] = c[1] + "," + c[2];
                                c[2] = c[5];
                                c[3] = c[4];
                            }
                        }
                        else if (c.size() == 5) // Proper format
                        {
                            c[2] = c[4];
                        }
                        else // Unreadable value count
                        {
                            delete newMovement;
                            continue;
                        }
                        // END - Get values from line

                        std::vector<std::string> d = StringHelper::split(c[1], ',');
                        std::vector<std::string> e = StringHelper::split(c[2], ',');


                        auto toMapIndex = EditEnvir->MapInfoList.FindIndex([&] (std::any a)
                        {
                            return a->FileName == c[3];
                        }); //check existing maps for the connection info
                        auto toMap = -1;

                        if (toMapIndex >= 0)
                        {
                            toMap = EditEnvir->MapInfoList[toMapIndex]->Index; //get real index
                        }
                        if (toMap < 0)
                        {
                            toMapIndex = EditEnvir->MapInfoList.FindIndex([&] (std::any a)
                            {
                                return a::FileName->ToString() == c[3];
                            });

                            if (toMapIndex >= 0)
                            {
                                toMap = EditEnvir->MapInfoList[toMapIndex]->Index;
                            }
                        }

                        if (toMap < 0)
                        {
                            delete newMovement;
                            continue;
                        }
                        newMovement->MapIndex = toMap;
                        newMovement->Source = new Point(std::stoi(d[0]), std::stoi(d[1]));
                        newMovement->Destination = new Point(std::stoi(e[0]), std::stoi(e[1]));
                        //NeedHole
                        //NeedMove
                        //ConquestIndex
                        EditEnvir->MapInfoList[j]->Movements.push_back(newMovement);

//C# TO C++ CONVERTER TODO TASK: A 'delete newMovement' statement was not added since newMovement was passed to a method or constructor. Handle memory management manually.
                    }
                }
                catch (const std::runtime_error &e1)
                {
                    continue;
                }
            }
        }
        for (int j = 0; j < EditEnvir->MapInfoList.size(); j++)
        {
            for (int k = 0; k < lines.size(); k++)
            {
                if (!StringHelper::startsWith(lines[k], "MINEZONE"))
                {
                    continue;
                }
                auto line = StringHelper::split(lines[k], ' ');

                try
                {
                    if (line[1] == EditEnvir->MapInfoList[j]->FileName)
                    {
                        MineZone *newMineInfo = new MineZone();
                        newMineInfo->Mine = StringHelper::fromString<unsigned char>(line[3]);
                        newMineInfo->Location = new Point(StringHelper::fromString<short>(line[4]), StringHelper::fromString<short>(line[5]));
                        newMineInfo->Size = StringHelper::fromString<unsigned short>(line[6]);
                        EditEnvir->MapInfoList[j]->MineZones.push_back(newMineInfo);

//C# TO C++ CONVERTER TODO TASK: A 'delete newMineInfo' statement was not added since newMineInfo was passed to a method or constructor. Handle memory management manually.
                    }
                }
                catch (const std::runtime_error &e2)
                {
                    continue;
                }
            }
        }
        for (int j = 0; j < EditEnvir->MapInfoList.size(); j++)
        {
            for (int k = 0; k < lines.size(); k++)
            {
                //STARTZONE(0,150,150,50) || SAFEZONE(0,150,150,50)
                if (!StringHelper::startsWith(lines[k], "SAFEZONE") && !StringHelper::startsWith(lines[k], "STARTZONE"))
                {
                    continue;
                }
                auto line = StringHelper::split(StringHelper::replace(lines[k], ")", ""), ','); // STARTZONE(0,150,150,50) -> STARTZONE(0 || 150 || 150 || 50
                auto head = StringHelper::split(line[0], '('); // STARTZONE(0 -> STARTZONE || 0
                try
                {
                    if (head[1] == EditEnvir->MapInfoList[j]->FileName)
                    {
                        MirDatabase::SafeZoneInfo *newSafeZone = new MirDatabase::SafeZoneInfo();
                        newSafeZone->Info = EditEnvir->MapInfoList[j];
                        newSafeZone->StartPoint = head[0] == "STARTZONE";
                        newSafeZone->Location = new Point(StringHelper::fromString<short>(line[2]), StringHelper::fromString<short>(line[3]));
                        newSafeZone->Size = StringHelper::fromString<unsigned short>(line[1]);
                        if (!std::find_if(EditEnvir->MapInfoList[j]->SafeZones.begin(), EditEnvir->MapInfoList[j]->SafeZones.end(), [&] (std::any sz)
                        {
                            return sz->StartPoint == newSafeZone->StartPoint && sz::Location->X == newSafeZone->Location->X && sz::Location->Y == newSafeZone->Location->Y;
                        }) != EditEnvir->MapInfoList[j]->SafeZones.end())
                        {
                            EditEnvir->MapInfoList[j]->SafeZones.push_back(newSafeZone);
                        }

//C# TO C++ CONVERTER TODO TASK: A 'delete newSafeZone' statement was not added since newSafeZone was passed to a method or constructor. Handle memory management manually.
                    }
                }
                catch (const std::runtime_error &e3)
                {
                    continue;
                }
            }
        }
    }

    void ConvertMapInfo::End()
    {
        SMain::Enqueue(std::string::Join("MapInfo Import Report:", errors.size() > 0 ? "" : "No Errors"));
        for (auto error : errors)
        {
            SMain::Enqueue(error);
        }
    }

std::vector<NPCInfo*> ConvertNPCInfo::NPCInfoList;

    void ConvertNPCInfo::Start()
    {
        std::string Path = "";

        OpenFileDialog *ofd = new OpenFileDialog();
        ofd->Filter = "Text File|*.txt";
        ofd->ShowDialog();

        if (ofd->FileName == "")
        {
            delete ofd;
            return;
        }

        Path = ofd->FileName;

        auto NPCList = File::ReadAllLines(Path);

        for (int i = 0; i < NPCList.size(); i++)
        {
            if (NPCList[i].find(';') != std::string::npos)
            {
                NPCList[i] = NPCList[i].substr(0, (int)NPCList[i].find(";", System::StringComparison::Ordinal));
            }

            auto Line = StringHelper::split(System::Text::RegularExpressions::Regex::Replace(NPCList[i], R"(\s+)", " "), ' ');

            if (Line.size() < 6)
            {
                continue;
            }

            try
            {
                NPCInfo *NPC = new NPCInfo();
                NPC->FileName = Line[0];
                NPC->Map = Line[1];
                NPC->X = StringHelper::fromString<short>(Line[2]);
                NPC->Y = StringHelper::fromString<short>(Line[3]);
                NPC->Title = Line[4];
                NPC->Image = (Line.size() >= 8) ? StringHelper::fromString<short>(Line[6]) : StringHelper::fromString<short>(Line[5]);

                NPCInfoList.push_back(NPC);

//C# TO C++ CONVERTER TODO TASK: A 'delete NPC' statement was not added since NPC was passed to a method or constructor. Handle memory management manually.
            }
            catch (const std::runtime_error &e1)
            {
                continue;
            }
        }

        delete ofd;
    }

    void ConvertNPCInfo::Stop()
    {
        NPCInfoList.clear();
    }

std::vector<MonGenInfo*> ConvertMonGenInfo::monGenList;

    void ConvertMonGenInfo::Start()
    {
        OpenFileDialog *ofd = new OpenFileDialog();
        ofd->Filter = "Text File|*.txt|Gen File|*.gen|All|*.*";
        ofd->Multiselect = true;
        ofd->ShowDialog();

        if (ofd->FileNames->Length == 0)
        {
            delete ofd;
            return;
        }

        for (int i = 0; i < ofd->FileNames->Length; i++)
        {
            auto MonGen = File::ReadAllLines(ofd->FileNames[i]);

            for (int j = 0; j < MonGen.size(); j++)
            {
                if (MonGen[j].find(';') != std::string::npos)
                {
                    MonGen[j] = MonGen[j].substr(0, (int)MonGen[j].find(";", System::StringComparison::Ordinal));
                }

                auto Line = StringHelper::split(System::Text::RegularExpressions::Regex::Replace(MonGen[j], R"(\s+)", " "), ' ');

                if (Line.size() < 7)
                {
                    continue;
                }

                try
                {
                    MonGenInfo *MonGenItem = new MonGenInfo();
                    MonGenItem->Map = Line[0];
                    MonGenItem->X = StringHelper::fromString<short>(Line[1]);
                    MonGenItem->Y = StringHelper::fromString<short>(Line[2]);
                    MonGenItem->Name = Line[3];
                    MonGenItem->Range = StringHelper::fromString<short>(Line[4]);
                    MonGenItem->Count = StringHelper::fromString<short>(Line[5]);
                    MonGenItem->Delay = StringHelper::fromString<short>(Line[6]);
                    MonGenItem->Direction = (Line.size() == 8) ? StringHelper::fromString<short>(Line[7]) : 0;

                    monGenList.push_back(MonGenItem);

//C# TO C++ CONVERTER TODO TASK: A 'delete MonGenItem' statement was not added since MonGenItem was passed to a method or constructor. Handle memory management manually.
                }
                catch (const std::runtime_error &e1)
                {
                    continue;
                }
            }
        }

        delete ofd;
    }

    void ConvertMonGenInfo::Stop()
    {
        monGenList.clear();
    }
}
